<template>
  <div class="chat-container">
    <!-- 顶部导航栏 -->
    <div class="header">
      <h1 class="title">医疗助手</h1>
    </div>

    <!-- 聊天历史 -->
    <div class="chat-history" ref="chatHistory">
      <div v-for="(msg, idx) in messages" :key="idx" :class="['message-wrapper', msg.role]">
        <div class="avatar" :class="msg.role">
          <span v-if="msg.role === 'user'">👤</span>
          <span v-else>🤖</span>
        </div>
        <div class="message-bubble" :class="msg.role">
          <div class="message-content">{{ msg.content }}</div>
        </div>
      </div>
    </div>

    <!-- 按钮区域上方遮罩 -->
    <div class="button-mask-top" :class="{ 'transparent': buttonTransparent }"></div>
    
    <!-- 底部输入区域 -->
    <div class="input-area">
      <div class="input-container">
        <button 
          class="voice-btn"
          @click="startRecord"
          :class="{ active: recording, 'transparent': buttonTransparent }"
          :disabled="sending">
          <svg class="mic-icon" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
            <path d="M12 15C13.66 15 15 13.66 15 12V6C15 4.34 13.66 3 12 3C10.34 3 9 4.34 9 6V12C9 13.66 10.34 15 12 15Z" 
                  stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
            <path d="M5.5 10.5V12C5.5 15.59 8.41 18.5 12 18.5C15.59 18.5 18.5 15.59 18.5 12V10.5" 
                  stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
            <path d="M12 18.5V22M8 22H16" 
                  stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
          </svg>
        </button>
      </div>
    </div>
    
    <!-- 按钮区域下方遮罩 -->
    <div class="button-mask-bottom" :class="{ 'transparent': buttonTransparent }"></div>

    <!-- 录音模态框 -->
    <transition name="modal-fade">
      <div v-if="showRecordingModal" class="modal-overlay" @click="cancelRecord">
        <div class="recording-modal" @click.stop>
          <div class="recording-content">
            <!-- 波形动画 -->
            <div class="waveform-container">
              <div class="waveform">
                <div v-for="i in 40" :key="i" class="wave-bar" 
                     :style="{ animationDelay: `${i * 0.05}s`, height: recording ? `${getBarHeight(i)}%` : '20%' }">
                </div>
              </div>
            </div>
            
            <div class="recording-text">{{ recordingText }}</div>
            
            <!-- 识别结果显示 - 实时显示 -->
            <div v-if="recognizedText" class="recognized-text-display">
              <div class="recognized-label">识别中...</div>
              <div class="recognized-content">{{ recognizedText }}</div>
            </div>

            <!-- 操作按钮 - 录音时始终显示 -->
            <div class="action-buttons">
              <button class="action-btn rerecord-btn" @click="reRecord">
                <span class="btn-icon">🔄</span>
                <span class="btn-text">重录</span>
              </button>
              <button class="action-btn send-btn-primary" @click="confirmAndSend" :disabled="!recognizedText || !recognizedText.trim()">
                <span class="btn-icon">✓</span>
                <span class="btn-text">发送</span>
              </button>
            </div>

            <!-- 提示文字 -->
            <div class="cancel-hint">
              点击空白处退出
            </div>
          </div>
        </div>
      </div>
    </transition>
  </div>
</template> 
 
<script setup>
import { ref, nextTick, onMounted, onUnmounted } from 'vue'

const messages = ref([
  { role: 'assistant', content: '您好！我是您的医疗助手，请点击下方按钮进行语音提问。' }
])
const recording = ref(false)
const sending = ref(false)
const showRecordingModal = ref(false)
const recognizedText = ref('')
const recordingText = ref('正在聆听...')
const chatHistory = ref(null)
const buttonTransparent = ref(false) // 标记按钮是否需要透明

let recognition = null
let eventSource = null // 用于 SSE 连接
let isRestarting = false // 标记是否正在重启识别（防止 onend 干扰）

function scrollToBottom() {
  nextTick(() => {
    if (chatHistory.value) {
      chatHistory.value.scrollTop = chatHistory.value.scrollHeight
    }
  })
}

// 检测按钮是否与内容重叠
function checkButtonOverlap() {
  if (!chatHistory.value) return
  
  const scrollContainer = chatHistory.value
  const scrollTop = scrollContainer.scrollTop
  const scrollHeight = scrollContainer.scrollHeight
  const clientHeight = scrollContainer.clientHeight
  
  // 计算是否滚动到底部（留一点容差，比如50px）
  const isAtBottom = scrollHeight - scrollTop - clientHeight < 50
  
  // 如果不在底部，说明用户在查看上方内容，按钮需要透明
  buttonTransparent.value = !isAtBottom
}

// 生成波形柱高度（模拟音频波动）
function getBarHeight(index) {
  if (!recording.value) return 20
  const time = Date.now() / 200
  const base = 20
  const amplitude = 60
  const frequency = 0.5
  return base + Math.abs(Math.sin(time + index * frequency)) * amplitude
}

// 点击按钮开始录音
function startRecord() {
  console.log('🎤 startRecord 被调用, recording:', recording.value, 'sending:', sending.value)
  
  if (recording.value || sending.value) {
    console.log('⚠️ 正在录音或发送中，忽略')
    return
  }
  
  console.log('👆 点击开始录音')
  
  // 立即开始录音
  startRecording()
}

// 实际开始录音
function startRecording() {
  console.log('🎤 startRecording 开始执行')
  
  if (recording.value) {
    console.log('⚠️ 已经在录音中，忽略')
    return
  }
  
  // 如果存在旧的 recognition 对象，先清理
  if (recognition) {
    try {
      recognition.abort()
      console.log('🧹 已清理旧的 recognition 对象')
    } catch (e) {
      console.log('⚠️ 清理旧 recognition 时出错:', e)
    }
    recognition = null
  }
  
  const SpeechRecognition = window.SpeechRecognition || window.webkitSpeechRecognition
  
  if (!SpeechRecognition) {
    alert('当前浏览器不支持语音识别，建议使用 Chrome 或 Edge 浏览器')
    return
  }

  // 显示录音模态框
  showRecordingModal.value = true
  recognizedText.value = ''
  recordingText.value = '正在聆听...'
  console.log('📋 模态框已显示')
  
  recognition = new SpeechRecognition()
  recognition.lang = 'zh-CN'
  recognition.interimResults = true
  recognition.continuous = true
  recognition.maxAlternatives = 1
  
  recognition.onstart = () => {
    recording.value = true
    console.log('🟢 recognition.onstart 触发, recording 设置为 true')
  }
  
  recognition.onresult = (event) => {
    let interimTranscript = ''
    let finalTranscript = ''
    
    for (let i = event.resultIndex; i < event.results.length; i++) {
      const transcript = event.results[i][0].transcript
      if (event.results[i].isFinal) {
        finalTranscript += transcript
      } else {
        interimTranscript += transcript
      }
    }
    
    // 实时更新识别中的文本
    // 优先使用 final 结果，否则使用 interim 结果
    if (finalTranscript) {
      recognizedText.value = finalTranscript
      console.log('🎯 Final 识别结果:', finalTranscript)
    } else if (interimTranscript) {
      recognizedText.value = interimTranscript
      console.log('📋 Interim 识别结果:', interimTranscript)
    }
  }
  
  recognition.onend = () => {
    console.log('🔚 recognition.onend 触发, isRestarting:', isRestarting)
    
    // 如果正在重启，忽略这个 onend 事件
    if (isRestarting) {
      console.log('⚠️ 正在重启识别，忽略 onend')
      return
    }
    
    // 录音正常持续，自动重启以保持连续识别
    if (recording.value) {
      console.log('⚠️ 录音持续中，自动重启识别')
      isRestarting = true
      setTimeout(() => {
        try {
          if (recording.value && recognition) {
            recognition.start()
            console.log('✅ 识别已重启（来自 onend）')
          }
        } catch (e) {
          console.log('❌ 重启识别失败:', e)
          recording.value = false
        } finally {
          isRestarting = false
        }
      }, 100)
      return
    }
    
    // 用户已停止录音，显示操作按钮
    console.log('🛑 录音已停止')
  }
  
  recognition.onerror = (event) => {
    console.error('❌ 语音识别错误:', event.error, 'isRestarting:', isRestarting)
    
    // 如果正在重启，忽略错误
    if (isRestarting) {
      console.log('⚠️ 正在重启识别，忽略错误')
      return
    }
    
    // 如果是 'no-speech' 错误且还在录音中，继续等待
    if (event.error === 'no-speech' && recording.value) {
      console.log('⚠️ 短时间未检测到语音，继续录音')
      recordingText.value = '请说话...'
      isRestarting = true
      setTimeout(() => {
        try {
          if (recording.value && recognition) {
            recognition.start()
            console.log('✅ 识别已重启（来自 onerror）')
          }
        } catch (e) {
          console.log('❌ 重启识别失败:', e)
          recording.value = false
          recordingText.value = '识别失败'
        } finally {
          isRestarting = false
        }
      }, 100)
      return
    }
    
    // 其他错误，停止录音
    recording.value = false
    
    if (event.error === 'no-speech') {
      recordingText.value = '未检测到语音'
    } else if (event.error === 'network') {
      recordingText.value = '网络错误，请检查连接'
    } else if (event.error === 'aborted') {
      recordingText.value = '已取消'
    } else if (event.error === 'not-allowed') {
      recordingText.value = '麦克风权限被拒绝，请在浏览器设置中允许麦克风访问'
    } else {
      recordingText.value = '识别失败: ' + event.error
    }
  }
  
  recognition.start()
}

// 停止录音（点击"重录"按钮时调用）
function stopRecording() {
  console.log('🛑 stopRecording 被调用, recording:', recording.value)
  
  if (!recording.value) {
    console.log('⚠️ 已经停止录音')
    return
  }
  
  // 停止录音状态
  recording.value = false
  
  // 停止语音识别
  if (recognition) {
    try {
      recognition.stop()
      console.log('🔴 recognition.stop() 已调用')
    } catch (e) {
      console.log('❌ 停止识别时出错:', e)
    }
  }
}

// 重新录音
function reRecord() {
  console.log('🔄 重新录音')
  
  // 停止当前录音
  stopRecording()
  
  // 清空识别文本
  recognizedText.value = ''
  recordingText.value = '正在聆听...'
  
  // 重新开始录音
  setTimeout(() => {
    if (recognition) {
      try {
        recognition.start()
        recording.value = true
        console.log('✅ 重新开始录音')
      } catch (e) {
        console.log('❌ 重新开始录音失败:', e)
      }
    }
  }, 200)
}

// 取消录音（关闭模态框）
function cancelRecord() {
  console.log('❌ 取消录音')
  
  // 如果正在录音，先停止
  if (recognition) {
    try {
      if (recording.value) {
        recognition.abort()
      }
    } catch (e) {
      console.log('⚠️ 停止识别时出错:', e)
    }
  }
  
  // 清空状态并关闭模态框
  recording.value = false
  showRecordingModal.value = false
  recognizedText.value = ''
  recordingText.value = ''
  isRestarting = false
  recognition = null
}

// 确认并发送
function confirmAndSend() {
  console.log('📤 确认并发送')
  
  if (!recognizedText.value.trim()) {
    console.log('⚠️ 没有识别到文本')
    return
  }
  
  const textToSend = recognizedText.value
  
  // 停止录音
  stopRecording()
  
  // 关闭模态框
  showRecordingModal.value = false
  recognizedText.value = ''
  isRestarting = false
  recognition = null
  
  // 发送消息
  sendMsg(textToSend)
}
 
function sendMsg(messageText) {
  if (!messageText || !messageText.trim()) return

  // 如果已经有正在进行的 SSE 连接，先关闭
  if (eventSource) {
    eventSource.close()
    eventSource = null
  }

  const userMessage = messageText.trim()
  messages.value.push({ role: 'user', content: userMessage })
  sending.value = true
  scrollToBottom()

  // 添加助手消息占位符
  const assistantMsgIndex = messages.value.length
  messages.value.push({ role: 'assistant', content: '' })
  scrollToBottom()

  // 打字机效果相关变量
  let textBuffer = [] // 存储待显示的字符队列
  let isTyping = false // 是否正在打字

  // 打字机效果函数 - 每次处理一批字符以提高性能
  const typeWriter = async () => {
    if (isTyping || textBuffer.length === 0) {
      return
    }

    isTyping = true
    const charsPerBatch = 2 // 每批显示的字符数，可调整流畅度

    while (textBuffer.length > 0) {
      // 从缓冲区取出一批字符
      const batchSize = Math.min(charsPerBatch, textBuffer.length)
      const batch = textBuffer.splice(0, batchSize)
      // ✅ 直接修改响应式数组中的对象，触发Vue更新
      messages.value[assistantMsgIndex].content += batch.join('')
      
      // 更新 DOM 并滚动
      await nextTick()
      scrollToBottom()
      
      // 控制打字速度（毫秒/批次）
      // 数字越小越快，建议 20-50
      await new Promise(resolve => setTimeout(resolve, 40))
    }

    isTyping = false
  }

  try {
    // 构建请求 URL，将消息作为查询参数（直连后端，避免代理缓冲）
    const backendBase = (import.meta.env && import.meta.env.VITE_BACKEND_URL)
      ? import.meta.env.VITE_BACKEND_URL
      : ((import.meta.env && import.meta.env.DEV) ? 'http://localhost:8080' : window.location.origin)
    const requestUrl = new URL('/api/qa', backendBase)
    requestUrl.searchParams.append('message', userMessage)

    // 使用原生 EventSource API（传入字符串 URL，确保兼容性）
    eventSource = new EventSource(requestUrl.toString())

    let messageCount = 0
    let lastReceivedTime = Date.now()
    
    eventSource.onmessage = async (event) => {
      messageCount++
      const now = Date.now()
      const timeDiff = now - lastReceivedTime
      lastReceivedTime = now
      
      if (event.data === '[DONE]') {
        console.log(`✅ SSE 完成，共收到 ${messageCount} 条消息`)
        eventSource.close()
        eventSource = null
        
        // 等待剩余字符全部打印完成
        while (textBuffer.length > 0 || isTyping) {
          await new Promise(resolve => setTimeout(resolve, 50))
        }
        
        sending.value = false
        scrollToBottom()
        return
      }

      console.log(`📨 [${messageCount}] 收到数据块 (${event.data.length} 字符, 距上次 ${timeDiff}ms):`, event.data)
      console.log(`   缓冲区状态: ${textBuffer.length} 字符待显示, isTyping: ${isTyping}`)
      
      // 将接收到的文本逐字符添加到缓冲区
      const chars = event.data.split('')
      textBuffer.push(...chars)
      
      console.log(`   添加后缓冲区: ${textBuffer.length} 字符`)
      
      // 启动打字机效果
      typeWriter()
    }

    eventSource.onerror = async (error) => {
      console.error('SSE错误:', error)
      if (eventSource) {
        eventSource.close()
        eventSource = null
      }
      
      // 等待缓冲区字符打印完成
      while (textBuffer.length > 0 || isTyping) {
        await new Promise(resolve => setTimeout(resolve, 50))
      }
      
      if (!messages.value[assistantMsgIndex].content) {
        messages.value[assistantMsgIndex].content = 'AI响应中断，请重试'
      }
      sending.value = false
      scrollToBottom()
    }

  } catch (error) {
    messages.value[assistantMsgIndex].content = '请求失败：' + error.message
    sending.value = false
    console.error('API调用失败:', error)
  }
}

onMounted(() => {
  scrollToBottom()
  
  // 监听聊天区域的滚动事件
  if (chatHistory.value) {
    chatHistory.value.addEventListener('scroll', checkButtonOverlap)
  }
  
  // 初始检测一次
  checkButtonOverlap()
})

onUnmounted(() => {
  // 移除滚动监听
  if (chatHistory.value) {
    chatHistory.value.removeEventListener('scroll', checkButtonOverlap)
  }
  
  // 关闭可能存在的 SSE 连接
  if (eventSource) {
    eventSource.close()
    eventSource = null
  }
  
  // 清理语音识别对象
  if (recognition) {
    try {
      recognition.abort()
    } catch (e) {
      console.log('清理识别对象时出错:', e)
    }
    recognition = null
  }
})
 </script> 
 
 <style scoped>
.chat-container {
  display: flex;
  flex-direction: column;
  height: 100vh;
  background: #ffffff;
}

/* 顶部导航栏 */
.header {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  height: 60px;
  background: #ffffff;
  border-bottom: 1px solid #e5e5e5;
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: 100;
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.02);
}

.title {
  margin: 0;
  font-size: 18px;
  font-weight: 600;
  color: #2d2d2d;
  letter-spacing: 0.5px;
}

/* 聊天历史区域 */
.chat-history {
  flex: 1;
  overflow-y: auto;
  padding: 80px 20px 200px; /* 👈 增大底部padding，确保内容不会被按钮遮挡，可根据需要调整这个值 */
  background: #f7f7f8;
}

.message-wrapper {
  display: flex;
  gap: 12px;
  margin-bottom: 20px;
  align-items: flex-start;
  max-width: 800px;
  margin-left: auto;
  margin-right: auto;
}

.message-wrapper.user {
  flex-direction: row-reverse;
}

.avatar {
  width: 36px;
  height: 36px;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 18px;
  flex-shrink: 0;
  background: #ffffff;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06);
}

.avatar.user {
  background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
}

.avatar.assistant {
  background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%);
}

.message-bubble {
  max-width: 70%;
  padding: 12px 16px;
  border-radius: 18px;
  word-wrap: break-word;
  box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);
}

.message-bubble.user {
  background: #007aff;
  color: #ffffff;
  border-bottom-right-radius: 4px;
}

.message-bubble.assistant {
  background: #ffffff;
  color: #2d2d2d;
  border: 1px solid #e5e5e5;
  border-bottom-left-radius: 4px;
}

.message-content {
  font-size: 15px;
  line-height: 1.6;
  white-space: pre-wrap;
}

/* 底部输入区域 */
.input-area {
  position: fixed;
  bottom: 80px; /* 调整这个值可以控制按钮向上的距离，数值越大越往上 */
  left: 0;
  right: 0;
  background: transparent; /* 去除白色背景 */
  border-top: none; /* 去除分割线 */
  padding: 16px 20px;
  z-index: 100;
}

.input-container {
  max-width: 800px;
  margin: 0 auto;
  display: flex;
  justify-content: center;
}

.voice-btn {
  width: 100px; /* 增大按钮尺寸，调整这个值可以改变按钮大小 */
  height: 100px; /* 增大按钮尺寸，调整这个值可以改变按钮大小 */
  border-radius: 50%;
  border: none;
  background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
  color: #ffffff;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
  transition: all 0.3s ease;
  box-shadow: 0 6px 20px rgba(102, 126, 234, 0.4);
  outline: none;
  opacity: 1;
}

/* 当内容滚动到按钮位置时，按钮变透明 */
.voice-btn.transparent {
  opacity: 0.15; /* 👈 透明度，可调整：0.1-0.3 之间，数值越小越透明 */
}

.voice-btn:active:not(:disabled) {
  transform: scale(0.95);
}

.voice-btn.active {
  background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%);
  animation: pulse 1.5s infinite;
  transform: scale(1);
  opacity: 1 !important; /* 录音时始终保持不透明 */
}

.voice-btn:disabled {
  opacity: 0.5;
  cursor: not-allowed;
}

.mic-icon {
  width: 44px; /* 按比例增大图标尺寸 */
  height: 44px; /* 按比例增大图标尺寸 */
  color: #ffffff;
}

@keyframes pulse {
  0%, 100% {
    box-shadow: 0 4px 12px rgba(102, 126, 234, 0.3);
  }
  50% {
    box-shadow: 0 4px 20px rgba(102, 126, 234, 0.6);
  }
}

/* 按钮区域遮罩 - 遮挡文字 */
.button-mask-top {
  position: fixed;
  bottom: 180px; /* 👈 按钮上方遮罩的起始位置，根据按钮位置调整 */
  left: 0;
  right: 0;
  height: 100px; /* 👈 上方遮罩高度，可调整 */
  background: linear-gradient(to bottom, transparent, #f7f7f8);
  z-index: 99;
  pointer-events: none; /* 不阻止点击穿透 */
  transition: opacity 0.3s ease;
  opacity: 1;
}

/* 滚动时遮罩也变透明 */
.button-mask-top.transparent {
  opacity: 0;
}

.button-mask-bottom {
  position: fixed;
  bottom: 0;
  left: 0;
  right: 0;
  height: 80px; /* 👈 下方遮罩高度，覆盖按钮底部区域 */
  background: #f7f7f8;
  z-index: 99;
  pointer-events: none; /* 不阻止点击穿透 */
  transition: opacity 0.3s ease;
  opacity: 1;
}

/* 滚动时遮罩也变透明 */
.button-mask-bottom.transparent {
  opacity: 0;
}

/* 录音模态框 */
.modal-overlay {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  background: rgba(0, 0, 0, 0.5);
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: 1000;
  backdrop-filter: blur(4px);
}

.recording-modal {
  background: #ffffff;
  border-radius: 20px;
  padding: 40px 30px;
  max-width: 400px;
  width: 90%;
  box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3);
}

.recording-content {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 20px;
}

/* 波形动画 */
.waveform-container {
  width: 100%;
  height: 120px;
  display: flex;
  align-items: center;
  justify-content: center;
  background: linear-gradient(135deg, #667eea15 0%, #764ba215 100%);
  border-radius: 16px;
  padding: 20px;
}

.waveform {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 3px;
  height: 100%;
}

.wave-bar {
  width: 3px;
  background: linear-gradient(180deg, #667eea 0%, #764ba2 100%);
  border-radius: 2px;
  transition: height 0.1s ease;
  animation: wave 1.2s ease-in-out infinite;
}

@keyframes wave {
  0%, 100% {
    transform: scaleY(1);
  }
  50% {
    transform: scaleY(1.5);
  }
}

.recording-text {
  font-size: 16px;
  color: #2d2d2d;
  font-weight: 500;
}

/* 识别结果显示 */
.recognized-text-display {
  width: 100%;
  background: #f7f7f8;
  border-radius: 12px;
  padding: 16px;
  max-height: 150px;
  overflow-y: auto;
}

.recognized-label {
  font-size: 13px;
  color: #6b6b6b;
  margin-bottom: 8px;
  font-weight: 500;
}

.recognized-content {
  font-size: 15px;
  color: #2d2d2d;
  line-height: 1.5;
  word-wrap: break-word;
}

/* 操作按钮 */
.action-buttons {
  display: flex;
  gap: 12px;
  width: 100%;
  margin-top: 10px;
}

.action-btn {
  flex: 1;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 6px;
  padding: 14px 8px;
  border: none;
  border-radius: 12px;
  background: #f7f7f8;
  color: #2d2d2d;
  cursor: pointer;
  transition: all 0.2s ease;
  font-size: 14px;
  outline: none;
}

.action-btn:hover {
  background: #e5e5e5;
  transform: translateY(-1px);
}

.action-btn:active {
  transform: translateY(0);
}

.rerecord-btn {
  color: #ff9500;
  border: 1px solid #ff950020;
}

.rerecord-btn:hover {
  background: #fff9f0;
  border-color: #ff950040;
}

.send-btn-primary {
  flex: 1.5;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 6px;
  padding: 14px 8px;
  border: none;
  border-radius: 12px;
  background: linear-gradient(135deg, #007aff 0%, #0051d5 100%);
  color: #ffffff;
  cursor: pointer;
  transition: all 0.2s ease;
  font-size: 14px;
  font-weight: 600;
  outline: none;
  box-shadow: 0 4px 12px rgba(0, 122, 255, 0.3);
}

.send-btn-primary:hover {
  transform: translateY(-2px);
  box-shadow: 0 6px 16px rgba(0, 122, 255, 0.4);
}

.send-btn-primary:active {
  transform: translateY(0);
  box-shadow: 0 2px 8px rgba(0, 122, 255, 0.3);
}

.send-btn-primary:disabled {
  opacity: 0.5;
  cursor: not-allowed;
  transform: none;
  box-shadow: 0 2px 8px rgba(0, 122, 255, 0.2);
}

.send-btn-primary:disabled:hover {
  transform: none;
  box-shadow: 0 2px 8px rgba(0, 122, 255, 0.2);
}

.btn-icon {
  font-size: 20px;
  font-weight: 600;
}

.btn-text {
  font-size: 13px;
}

.cancel-hint {
  font-size: 13px;
  color: #6b6b6b;
  text-align: center;
  margin-top: 8px;
}

/* 模态框动画 */
.modal-fade-enter-active,
.modal-fade-leave-active {
  transition: opacity 0.3s ease;
}

.modal-fade-enter-from,
.modal-fade-leave-to {
  opacity: 0;
}

.modal-fade-enter-active .recording-modal,
.modal-fade-leave-active .recording-modal {
  transition: transform 0.3s ease;
}

.modal-fade-enter-from .recording-modal,
.modal-fade-leave-to .recording-modal {
  transform: scale(0.9);
}

/* 响应式设计 */
@media (max-width: 768px) {
  .message-bubble {
    max-width: 85%;
  }
  
  .recording-modal {
    width: 95%;
    padding: 30px 20px;
  }
}

/* 滚动条样式 */
.chat-history::-webkit-scrollbar {
  width: 6px;
}

.chat-history::-webkit-scrollbar-track {
  background: transparent;
}

.chat-history::-webkit-scrollbar-thumb {
  background: #d1d1d6;
  border-radius: 3px;
}

.chat-history::-webkit-scrollbar-thumb:hover {
  background: #b1b1b6;
}
</style>